package cn.tedu.charging.device.dao.repository.impl;

import cn.tedu.charging.common.constant.CacheKeyConst;
import cn.tedu.charging.common.pojo.query.NearStationsQuery;
import cn.tedu.charging.device.dao.mapper.GunMapper;
import cn.tedu.charging.device.dao.mapper.StationMapper;
import cn.tedu.charging.device.dao.repository.DeviceRepository;
import cn.tedu.charging.device.pojo.po.ChargingGunInfoPO;
import cn.tedu.charging.device.pojo.po.ChargingStationPO;
import cn.tedu.charging.device.pojo.po.StationCanalPO;
import cn.tedu.charging.device.pojo.po.StationGeoPO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.stream.Collectors;

@Repository
@Slf4j
public class DeviceRepositoryImpl implements DeviceRepository {
    @Autowired
    private StationMapper stationMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private GunMapper gunMapper;


    @Override
    public List<StationGeoPO> nearStations(NearStationsQuery query) {
        GeoOperations<String,Integer> geoOps = redisTemplate.opsForGeo();
        Circle circle =
                new Circle(
                        new Point(query.getLongitude(),query.getLatitude()),
                        new Distance(query.getRadius(),RedisGeoCommands.DistanceUnit.KILOMETERS));

        RedisGeoCommands.GeoRadiusCommandArgs args =
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                        .includeCoordinates()
                        .includeDistance();
        GeoResults<RedisGeoCommands.GeoLocation<Integer>> geoResults =
                geoOps.radius(CacheKeyConst.GEO_STATIONS, circle, args);

        List<GeoResult<RedisGeoCommands.GeoLocation<Integer>>> results = geoResults.getContent();

        List<StationGeoPO> pos = null;

        if (results != null && results.size() > 0){
            pos = results.stream().map(result -> {
                //1.从Redis中获取数据
                //获取距离
                Distance distance = result.getDistance();//16.762km
                //距离值取小数点2位
                BigDecimal distValue=new BigDecimal(distance.getValue()+"").setScale(2, RoundingMode.HALF_UP);
                //获取stationId
                Integer stationId=result.getContent().getName();
                //经度、纬度
                double lng = result.getContent().getPoint().getX();
                double lat = result.getContent().getPoint().getY();
                //2.从数据库中获取充电站信息
                ChargingStationPO stationPO = getStationById(stationId);
                String stationName=stationPO.getStationName();
                Integer stationStatus=stationPO.getStationStatus();
                //3.封装数据
                StationGeoPO po=new StationGeoPO();
                po.setStationId(stationId);
                po.setStationName(stationName);
                po.setStationStatus(stationStatus);
                po.setStationLng(lng);
                po.setStationLat(lat);
                po.setDistance(distValue);
                return po;
            }).collect(Collectors.toList());
        }

        return pos;
    }

    @Override
    public String getStationName(Integer stationId) {
        return "";
    }

    @Override
    public ChargingStationPO getStationById(Integer stationId) {
        //拿到一个 redis操作string数据结构的客户端对象 设置key value泛型
        ValueOperations<String,ChargingStationPO> valueOps = redisTemplate.opsForValue();
        //操作缓存读写使用key
        String stationCacheKey=CacheKeyConst.STATION_DETAIL_PREFIX+stationId;
        //1.读取缓存 get key
        ChargingStationPO stationPO = valueOps.get(stationCacheKey);
        //判断没命中
        if(stationPO==null){
            log.debug("场站缓存数据未命中,场站id:{}",stationId);
            //3.读取数据库
            stationPO = stationMapper.selectById(stationId);
            //4.保证下次读取可以命中缓存 需要使用它恢复缓存数据 回填
            valueOps.set(stationCacheKey,stationPO);
        }else{
            log.debug("场站缓存数据命中,场站id:{}",stationId);
        }
        return stationPO;
    }

    @Override
    public List<ChargingGunInfoPO> getStationGuns(Integer stationId) {
        List<ChargingGunInfoPO> guns = gunMapper.selectByStationId(stationId);
        return guns;
    }

    @Override
    public Boolean updateGunStatus(Integer gunId, Integer status) {
        return null;
    }

    @Override
    public void saveStation(StationCanalPO stationCanalPO) {

    }

    @Override
    public void updateStation(StationCanalPO before, StationCanalPO after) {

    }

    @Override
    public void deleteStation(StationCanalPO stationCanalPO) {

    }

    @Override
    public Long countGunByIdAndStatus(Integer gunId, Integer status) {
        return 0L;
    }

    @Override
    public List<ChargingStationPO> getStations() {
        //SELECT * FROM charging_stations
        return stationMapper.selectList(null);
    }

    @Override
       /**
     * 批量保存充电站地理位置信息到Redis
     *
     * @param pos 充电站位置信息列表，包含充电站ID、经度、纬度等信息
     */
    public void saveGeos(List<ChargingStationPO> pos) {
        GeoOperations geoOps = redisTemplate.opsForGeo();

        List<RedisGeoCommands.GeoLocation> geoLocations = null;
        // 将充电站PO对象转换为Redis地理位置对象
        geoLocations = pos.stream().map(po -> {
            //创建GeoLocation,将数据po映射封装
            RedisGeoCommands.GeoLocation geoLocation =
                    new RedisGeoCommands.GeoLocation(po.getId(), new Point(po.getStationLng().doubleValue(), po.getStationLat().doubleValue()));
            return geoLocation;
        }).collect(Collectors.toList());
        //批量保存,写入Redis
        redisTemplate.opsForGeo().add(CacheKeyConst.GEO_STATIONS, geoLocations);
    }

    @Override
    public Boolean geoStationsExists() {

        return redisTemplate.hasKey(CacheKeyConst.GEO_STATIONS);
    }

    @Override
    public Long countAll() {
        return stationMapper.selectCount(null);
    }

    @Override
    public List<ChargingStationPO> getStationsPage(Long start, Long rows) {
        //select * from charging_station limit start,rows
        QueryWrapper<ChargingStationPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.last("limit " + start + "," + rows);
        return stationMapper.selectList(queryWrapper);
    }


}





























